home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / DCLAP 4j / network / ncsasock / sock_tcp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-17  |  21.4 KB  |  972 lines  |  [TEXT/R*ch]

  1. /*
  2.  * BSD-style socket emulation library for the Mac
  3.  * Original author: Tom Milligan
  4.  * Current author: Charlie Reiman - creiman@ncsa.uiuc.edu
  5.  *
  6.  * This source file is placed in the public domian.
  7.  * Any resemblance to NCSA Telnet, living or dead, is purely coincidental.
  8.  *
  9.  *      National Center for Supercomputing Applications
  10.  *      152 Computing Applications Building
  11.  *      605 E. Springfield Ave.
  12.  *      Champaign, IL  61820
  13.  */
  14.  
  15. #ifdef USEDUMP
  16. # pragma load "Socket.dump"
  17.  
  18. #else
  19. # include <Events.h>
  20. # include <Memory.h>
  21. # include <Types.h>
  22. # include <OSUtils.h> /* for SysBeep */
  23. # include <Events.h> /* for TickCount */
  24. # include <Stdio.h>
  25.  
  26.  
  27. # include <s_types.h>
  28. # include <neti_in.h>
  29. #ifdef COMP_CODEWAR
  30. #define NOWAY
  31. #undef EDOM
  32. #undef ERANGE
  33. #endif
  34. # include <neterrno.h>
  35. # include <s_socket.h>
  36. # include <s_time.h>
  37. # include <s_uio.h>
  38.  
  39. # include "sock_str.h"
  40. # include "sock_int.h" 
  41. # include <unixlib.h>
  42.  
  43. #endif
  44.  
  45. #ifndef THINK_C
  46. # include "sock_int.h"
  47. #endif
  48.  
  49. extern SocketPtr sockets;
  50. extern SpinFn spinroutine;    /* The spin routine. */ 
  51.  
  52. /*
  53.  * sock_tcp_new_stream
  54.  *
  55.  * Create a new tcp stream.
  56.  */
  57. int sock_tcp_new_stream(
  58.     SocketPtr sp)
  59. {
  60.     OSErr        io;
  61.     TCPiopb        pb;
  62.     StreamHashEntPtr shep;
  63.  
  64. #if SOCK_TCP_DEBUG >= 2
  65.     sock_print("sock_tcp_new_stream", sp);
  66. #endif
  67.     
  68.     io = xTCPCreate(STREAM_BUFFER_SIZE, sock_tcp_notify, &pb);
  69.     switch(io)
  70.     {
  71.         case noErr:                 break;
  72.         case streamAlreadyOpen:     return(sock_err(io));
  73.         case invalidLength:         return(sock_err(ENOBUFS));
  74.         case invalidBufPtr:         return(sock_err(ENOBUFS));
  75.         case insufficientResources: return(sock_err(EMFILE));
  76.         default: /* error from PBOpen */ return(sock_err(ENETDOWN));
  77.     }
  78.     sp->sstate = SOCK_STATE_UNCONNECTED;
  79.     sp->peer.sin_family = AF_INET;
  80.     sp->peer.sin_addr.s_addr = 0;
  81.     sp->peer.sin_port = 0;
  82.     bzero(&sp->peer.sin_zero[0], 8);
  83.     sp->dataavail = 0;
  84.     sp->asyncerr = 0;
  85.     sp->stream = pb.tcpStream;
  86.         
  87.     if ((shep = sock_new_shep(sp->stream))!=NULL)
  88.         {
  89.         shep -> stream = sp->stream;
  90.         shep -> socket = sp;
  91.         return(0);
  92.         }
  93.     else
  94.         return -1;
  95. }
  96.  
  97.  
  98. /*
  99.  *    sock_tcp_connect - initiate a connection on a TCP socket
  100.  *
  101.  */
  102. static void sock_tcp_connect_done(TCPiopb *pb);
  103.  
  104. int sock_tcp_connect(
  105.     SocketPtr sp,
  106.     struct sockaddr_in *addr)
  107. {
  108.     OSErr io;
  109.     TCPiopb    *pb;
  110.     
  111. #if SOCK_TCP_DEBUG >= 2
  112.     sock_print("sock_tcp_connect",sp);
  113. #endif
  114.     
  115.     /* Make sure this socket can connect. */
  116.     if (sp->sstate == SOCK_STATE_CONNECTING)
  117.         return(sock_err(EALREADY));
  118.     if (sp->sstate != SOCK_STATE_UNCONNECTED)
  119.         return(sock_err(EISCONN));
  120.         
  121.     sp->sstate = SOCK_STATE_CONNECTING;
  122.     
  123.     if (!(pb= (TCPiopb *)sock_fetch_pb(sp)))
  124.         return sock_err(ENOMEM);
  125.     
  126.     io = xTCPActiveOpen(pb, sp->sa.sin_port,addr->sin_addr.s_addr, addr->sin_port, 
  127.             (TCPIOCompletionProc) sock_tcp_connect_done);
  128.             
  129.     if (io != noErr)
  130.     {
  131.         sp->sstate = SOCK_STATE_UNCONNECTED;
  132.         return(sock_err(io));
  133.     }
  134.     
  135.     if (sp->nonblocking)        
  136.         return(sock_err(EINPROGRESS));
  137.     
  138.     /* sync connect - spin till TCPActiveOpen completes */
  139.  
  140. #if SOCK_TCP_DEBUG >= 5
  141.     dprintf("spinning in connect\n");
  142. #endif
  143.  
  144.     SPIN (pb->ioResult==inProgress,SP_MISC,0L)
  145.  
  146. #if SOCK_TCP_DEBUG >= 5
  147.     dprintf("done spinning\n");
  148. #endif
  149.     
  150.     if ( pb->ioResult != noErr )
  151.         return sock_err(pb->ioResult);
  152.     else
  153.         return 0;
  154. }
  155.  
  156. /*
  157.  * sock_tcp_listen() - put s into the listen state.
  158.  */
  159. int sock_tcp_listen(
  160.     SocketPtr sp)
  161. {
  162.     OSErr        io;
  163.     TCPiopb        *pb;
  164.     void sock_tcp_listen_done(TCPiopb *pb);
  165.  
  166. #if SOCK_TCP_DEBUG >= 2
  167.     sock_print("sock_tcp_listen",sp);
  168. #endif
  169.  
  170.     if (!(pb = (TCPiopb *)sock_fetch_pb(sp)))
  171.         return (sock_err(ENOMEM));
  172.     
  173.     if (sp->sstate != SOCK_STATE_UNCONNECTED)
  174.         return(sock_err(EISCONN));
  175.     
  176.     sp->sstate = SOCK_STATE_LISTENING;
  177.     
  178.     io = xTCPPassiveOpen(pb, sp->sa.sin_port, (TCPIOCompletionProc) sock_tcp_listen_done);
  179.     if (io != noErr) 
  180.     {
  181.         sp->sstate = SOCK_STATE_UNCONNECTED;
  182.         return(sock_err(io));
  183.     }
  184. #if SOCK_TCP_DEBUG >= 5
  185.     dprintf("sock_tcp_listen: about to spin for port number - ticks %d\n",
  186.             TickCount());
  187. #endif
  188.     while (pb->csParam.open.localPort == 0)
  189.     {
  190. #if SOCK_TCP_DEBUG >= 5
  191.         tcpCheckNotify();
  192. #endif
  193.         SPIN(false,SP_MISC,0L)
  194.     }
  195. #if SOCK_TCP_DEBUG >= 5
  196.     dprintf("sock_tcp_listen: port number is %d ticks %d\n",
  197.     pb->csParam.open.localPort,TickCount());
  198. #endif
  199.     sp->sa.sin_addr.s_addr = pb->csParam.open.localHost;
  200.     sp->sa.sin_port = pb->csParam.open.localPort;
  201.     return(0);
  202. }
  203.  
  204. /*
  205.  *    sock_tcp_accept()
  206.  */
  207. int sock_tcp_accept(
  208.     SocketPtr sp,
  209.     struct sockaddr_in *from,
  210.     Int4 *fromlen)
  211. {
  212.     int         s1;
  213.     TCPiopb        *pb;
  214.     StreamHashEntPtr shep;
  215.  
  216. #if SOCK_TCP_DEBUG >= 2
  217.     sock_print("sock_tcp_accept",sp);
  218. #endif
  219.  
  220.     if (sp->sstate == SOCK_STATE_UNCONNECTED)
  221.     {
  222.         if (sp->asyncerr != 0)
  223.         {
  224.             (void) sock_err(sp->asyncerr);
  225.             sp->asyncerr = 0;
  226.             return(-1);
  227.         }
  228.         else
  229.             return(sock_err(ENOTCONN));
  230.     }
  231.     if (sp->sstate != SOCK_STATE_LISTENING && sp->sstate != SOCK_STATE_LIS_CON)
  232.         return(sock_err(ENOTCONN));
  233.  
  234.     if (sp->sstate == SOCK_STATE_LISTENING) 
  235.     {    
  236.         if (sp->nonblocking) 
  237.             return(sock_err(EWOULDBLOCK));
  238.  
  239.         /*    Spin till sock_tcp_listen_done runs. */
  240. #if SOCK_TCP_DEBUG >= 5
  241.         dprintf("--- blocking...\n");
  242. #endif
  243.         
  244.         SPIN(sp->sstate == SOCK_STATE_LISTENING,SP_MISC,0L);
  245.  
  246. #if SOCK_TCP_DEBUG >= 5
  247.         dprintf("--- done blocking...\n");
  248. #endif
  249.         
  250.         /* got notification - was it success? */
  251.         if (sp->sstate != SOCK_STATE_LIS_CON) 
  252.         {
  253. #if    SOCK_TCP_DEBUG >=3
  254.             dprintf("--- failed state %04x code %d\n",sp->sstate,sp->asyncerr);
  255. #endif
  256.             (void) sock_err(sp->asyncerr);
  257.             sp->asyncerr = 0;
  258.             return(-1);
  259.         }
  260.     }
  261. #if SOCK_TCP_DEBUG >= 3
  262.     dprintf("sock_tcp_accept: Have connection, peer is %08x/%d, duplicating socket.\n",
  263.             sp->peer.sin_addr,sp->peer.sin_port);
  264. #endif
  265.     /*
  266.      * Have connection.  Duplicate this socket.  The client gets the connection
  267.      * on the new socket and I create a new stream on the old socket and put it 
  268.      * in listen state. 
  269.      */
  270.     sp->sstate = SOCK_STATE_CONNECTED;
  271.  
  272.     s1 = sock_free_fd(0);
  273.     if (s1 < 0) 
  274.     {
  275.         /*    No descriptors left.  Abort the incoming connection. */
  276. #if SOCK_TCP_DEBUG >= 2
  277.         dprintf("sock_tcp_accept: No descriptors left.\n");
  278. #endif
  279.         if (!(pb = (TCPiopb *)sock_fetch_pb (sp)))
  280.             return sock_err(ENOMEM);
  281.             
  282.         (void) xTCPAbort(pb);
  283.         sp->sstate = SOCK_STATE_UNCONNECTED;
  284.  
  285.         /* try and put the socket back in listen mode */
  286.         if (sock_tcp_listen(sp) < 0) 
  287.         {
  288. #if SOCK_TCP_DEBUG >= 1
  289.             dprintf("sock_tcp_accept: sock_tcp_listen fails\n");
  290. #endif
  291.             sp->sstate = SOCK_STATE_UNCONNECTED;
  292.             return(-1);        /* errno already set */
  293.         }
  294.         return(sock_err(EMFILE));
  295.     }
  296.     
  297.     /* copy the incoming connection to the new socket */
  298.     sock_dup_fd(sp->fd,s1);
  299. #if SOCK_TCP_DEBUG >= 3
  300.     dprintf("sock_tcp_accept: new socket is %d\n",s1);
  301.     sock_dump();
  302. #endif
  303.  
  304.     /* quitely adjust the StreamHash table */
  305.     if ((shep = sock_find_shep(sp->stream))!=NULL)
  306.         {
  307.         shep->socket = sockets+s1;        /* point to new socket */
  308.         }
  309.  
  310.     /* Create a new MacTCP stream on the old socket and put it into */
  311.     /* listen state to accept more connections. */
  312.     if (sock_tcp_new_stream(sp) < 0 || sock_tcp_listen(sp) < 0) 
  313.     {
  314. #if SOCK_TCP_DEBUG >= 2
  315.         dprintf("accept: failed to restart old socket\n");
  316. #endif
  317.         /* nothing to listen on */
  318.         sp->sstate = SOCK_STATE_UNCONNECTED;
  319.         
  320.         /* kill the incoming connection */
  321.         if (!(pb= (TCPiopb *)sock_fetch_pb(sockets+s1)))
  322.             return sock_err(ENOMEM);
  323.             
  324.         xTCPRelease(pb);
  325.         sock_clear_fd(s1);
  326.         
  327.         return(-1); /* errno set */
  328.     }
  329. #if SOCK_TCP_DEBUG >= 3
  330.     dprintf("sock_tcp_accept: got new stream\n");
  331.     sock_dump();
  332. #endif
  333.  
  334.     /* return address of partner */
  335.     sock_copy_addr(&sockets[s1].peer, from, fromlen);
  336.  
  337.     return(s1);
  338. }
  339.  
  340. /*
  341.  *    sock_tcp_accept_once()
  342.  */
  343. int sock_tcp_accept_once(
  344.     SocketPtr sp,
  345.     struct sockaddr_in *from,
  346.     Int4 *fromlen)
  347. {
  348.  
  349. #if SOCK_TCP_DEBUG >= 2
  350.     sock_print("sock_tcp_accept_once",sp);
  351. #endif
  352.  
  353.     if (sp->sstate == SOCK_STATE_UNCONNECTED)
  354.     {
  355.         if (sp->asyncerr != 0)
  356.         {
  357.             (void) sock_err(sp->asyncerr);
  358.             sp->asyncerr = 0;
  359.             return(-1);
  360.         }
  361.         else
  362.             return(sock_err(ENOTCONN));
  363.     }
  364.     if (sp->sstate != SOCK_STATE_LISTENING && sp->sstate != SOCK_STATE_LIS_CON)
  365.         return(sock_err(ENOTCONN));
  366.  
  367.     if (sp->sstate == SOCK_STATE_LISTENING) 
  368.     {    
  369.         if (sp->nonblocking) 
  370.             return(sock_err(EWOULDBLOCK));
  371.  
  372.         /*    Spin till sock_tcp_listen_done runs. */
  373. #if SOCK_TCP_DEBUG >= 5
  374.         dprintf("--- blocking...\n");
  375. #endif
  376.         
  377.         SPIN(sp->sstate == SOCK_STATE_LISTENING,SP_MISC,0L);
  378.         
  379. #if SOCK_TCP_DEBUG >= 5
  380.         dprintf("--- done blocking...\n");
  381. #endif
  382.         
  383.         /* got notification - was it success? */
  384.         if (sp->sstate != SOCK_STATE_LIS_CON) 
  385.         {
  386. #if    SOCK_TCP_DEBUG >=3
  387.             dprintf("--- failed state %04x code %d\n",sp->sstate,sp->asyncerr);
  388. #endif
  389.             (void) sock_err(sp->asyncerr);
  390.             sp->asyncerr = 0;
  391.             return(-1);
  392.         }
  393.     }
  394. #if SOCK_TCP_DEBUG >= 3
  395.     dprintf("sock_tcp_accept_once: Have connection, peer is %08x/%d.\n",
  396.             sp->peer.sin_addr,sp->peer.sin_port);
  397. #endif
  398.     /*
  399.      * Have connection.
  400.      */
  401.     sp->sstate = SOCK_STATE_CONNECTED;
  402.     
  403.     /* return address of partner */
  404.     sock_copy_addr(&(sp->peer), from, fromlen);
  405.  
  406.     return(0);
  407. }
  408.  
  409.  
  410. static void sock_tcp_recv_done( TCPiopb *pb);
  411.  
  412. /*
  413.  * sock_tcp_recv()
  414.  *
  415.  * returns bytes received or -1 and errno
  416.  */
  417. int sock_tcp_recv(
  418.     SocketPtr sp,
  419.     char *buffer,
  420.     int buflen,
  421.     int flags)
  422. {
  423. #pragma unused(flags)
  424.     TCPiopb    *pb;
  425.     int iter; /* iteration */
  426.     
  427. #if SOCK_TCP_DEBUG >= 2
  428.     sock_print("sock_tcp_recv",sp);
  429. #endif
  430.  
  431.     /* socket hasn't finished connecting yet */
  432.     if (sp->sstate == SOCK_STATE_CONNECTING)
  433.     {
  434. #if SOCK_TCP_DEBUG >= 5
  435.         dprintf("sock_tcp_recv: connection still in progress\n");
  436. #endif
  437.         if (sp->nonblocking)
  438.             return(sock_err(EWOULDBLOCK));
  439.             
  440.         /* async connect and sync recv? */
  441. #if SOCK_TCP_DEBUG >= 5
  442.         dprintf("sock_tcp_recv: spinning on connect\n");
  443. #endif
  444.  
  445.         SPIN(sp->sstate == SOCK_STATE_CONNECTING,SP_MISC,0L)
  446.  
  447. #if SOCK_TCP_DEBUG >= 5
  448.         dprintf("sock_tcp_recv: done spinning\n");
  449. #endif
  450.     }
  451.         
  452.     /* socket is not connected */
  453.     if (! (sp->sstate == SOCK_STATE_CONNECTED)) 
  454.     {
  455.         /* see if the connect died (pretty poor test) */
  456.         if (sp->sstate == SOCK_STATE_UNCONNECTED && sp->asyncerr != 0)
  457.         {
  458.             (void) sock_err(sp->asyncerr);
  459.             sp->asyncerr = 0;
  460.             return(-1);
  461.         }
  462.  
  463.         /* I guess he just forgot */
  464.         return(sock_err(ENOTCONN));        
  465.     }
  466.             
  467.     if (sp->dataavail == 0)
  468.         sp->dataavail = xTCPBytesUnread(sp);        /* sync. call */
  469. #if SOCK_TCP_DEBUG >= 3
  470.     dprintf("sock_tcp_recv: %d bytes available\n", sp->dataavail);
  471. #endif
  472.     if (sp->nonblocking && sp->dataavail == 0) 
  473.             return(sock_err(EWOULDBLOCK));
  474.  
  475.     sp->torecv              = 1; /* # of bytes to try to receive */
  476.     sp->recvBuf             = buffer;
  477.     sp->recvd               = 0; /* count of bytes received */    
  478.     pb = (TCPiopb *)sock_fetch_pb(sp);
  479.     
  480.     /* make 2 iterations; on 1st try, read 1 byte; on 2nd, read
  481.        all outstanding available bytes, up to buflen ... this mechanism seems to
  482.        be necessary for decent performance, because TCPRcv only completes when one
  483.        of the following takes place:
  484.        * enough data has arrived to fill the receive buffer
  485.        * pushed data arrives
  486.        * urgent data is outstanding
  487.        * some reasonable period passes after the arrival of nonpushed, nonurgent data
  488.        * the amount of data received is greater than or equal to 25 percent of the total
  489.          receive buffering for this stream
  490.        * the command time-out expires
  491.        
  492.        In the case when a caller has requested N bytes, and the data is "normal" TCP
  493.        data, the "reasonable" period must expire before this function will return. This
  494.        "reasonable" period appears to be about one second (MacTCP version 1.1), and is
  495.        not configurable. The hope in the algorithm implemented here is that a reasonable
  496.        amount of data will arrive along with the first byte, and that the caller is
  497.        capable of issuing another read() to obtain more data. The one-second "reasonable"
  498.        delay is thus eliminated.
  499.        
  500.        J. Epstein, NCBI, 06/24/92
  501.     */
  502.     
  503.     for (iter = 0; iter < 2; iter++) {
  504.  
  505.         sp->asyncerr            = inProgress;
  506.     
  507.         xTCPRcv(pb, sp->recvBuf, min (sp->torecv,TCP_MAX_MSG),0, 
  508.                 (TCPIOCompletionProc) sock_tcp_recv_done);
  509.     
  510.         SPIN(sp->torecv&&(pb->ioResult==noErr||pb->ioResult==inProgress),
  511.             SP_TCP_READ,sp->torecv)
  512.         
  513.         if ( pb->ioResult == commandTimeout )
  514.             pb->ioResult = noErr;
  515.  
  516.         switch(pb->ioResult)
  517.         {
  518.             case noErr:
  519. #if SOCK_TCP_DEBUG >= 3
  520.                 dprintf("sock_tcp_recv: got %d bytes\n", sp->recvd);
  521. #endif
  522.                 sp->dataavail = xTCPBytesUnread(sp);
  523.                 sp->asyncerr = noErr;
  524.                 if (sp->dataavail <= 0 || buflen <= 1 || iter == 1)
  525.                     return(sp->recvd);
  526.                 /* loop back and obtain the remaining outstanding data */
  527.                 sp->torecv = min(sp->dataavail, buflen - 1);
  528.                 sp->recvBuf = &buffer[1];
  529.                 break;
  530.                 
  531.             case connectionClosing:
  532. #if SOCK_TCP_DEBUG >= 2
  533.                 dprintf("sock_tcp_recv: connection closed\n");
  534. #endif
  535.                 return (sp->recvd);
  536.                 break;
  537.  
  538.  
  539.             case connectionTerminated:
  540.                 /* The connection is aborted. */
  541.                 sp->sstate = SOCK_STATE_UNCONNECTED;
  542. #if SOCK_TCP_DEBUG >= 1
  543.                 dprintf("sock_tcp_recv: connection gone!\n");
  544. #endif
  545.                 return(sock_err(ENOTCONN));
  546.  
  547.             case commandTimeout: /* this one should be caught by sock_tcp_recv_done */
  548.             case connectionDoesntExist:
  549.             case invalidStreamPtr:
  550.             case invalidLength:
  551.             case invalidBufPtr:
  552.             default:
  553.                 return(sock_err(pb->ioResult));
  554.         }
  555.     }
  556.     return(sock_err(ENOTCONN));
  557. }
  558.  
  559. /*
  560.  *    sock_tcp_can_read() - returns non-zero if data or a connection is available
  561.  *  must also return one if the connection is down to force an exit from the
  562.  *  select routine (select is the only thing that uses this).
  563.  */
  564. int sock_tcp_can_read(SocketPtr sp)
  565.     {
  566.     TCPiopb        *pb;
  567.     
  568.     if (sp->sstate == SOCK_STATE_LIS_CON) 
  569.         return(1);
  570.  
  571.     else if (sp->sstate == SOCK_STATE_CONNECTED) 
  572.         {
  573.         if (!(pb= (TCPiopb *)sock_fetch_pb(sp)))
  574.             return sock_err (ENOMEM);
  575.         
  576.         sp->dataavail = xTCPBytesUnread(sp);
  577.         if (sp->dataavail > 0) 
  578.             return(1);
  579.         }
  580.     else if ( ( sp->sstate == SOCK_STATE_UNCONNECTED ) || 
  581.             ( sp->sstate == SOCK_STATE_CLOSING ) )
  582.         return 1;
  583.  
  584.     return(0);
  585.     }
  586.  
  587. /* avoid using standard library here because size of an int may vary externally */
  588. void *sock_memset(void *s, int c, size_t n)
  589. {
  590.     char *t = (char*)s;
  591.     
  592.     for (; n > 0; n--, t++)
  593.         *t = c;
  594.     return s;
  595. }
  596.     
  597. static void sock_tcp_send_done( TCPiopb *pb);
  598.  
  599. /*
  600.  *    sock_tcp_send() - send data
  601.  *
  602.  *    returns bytes sent or -1 and errno
  603.  */
  604. int sock_tcp_send(
  605.     SocketPtr sp,
  606.     char *buffer,
  607.     int count,
  608.     int flags)
  609. {
  610.     int        bytes,towrite;
  611.     miniwds    *thiswds;
  612.     short    wdsnum;
  613.     TCPiopb    *pb;
  614.     miniwds    wdsarray[TCP_MAX_WDS];
  615.  
  616. #if SOCK_TCP_DEBUG >= 2
  617.     sock_print("sock_tcp_send",sp);
  618. #endif
  619.  
  620.     /* socket hasn't finished connecting yet */
  621.     if (sp->sstate == SOCK_STATE_CONNECTING)
  622.     {
  623. #if SOCK_TCP_DEBUG >= 5
  624.         dprintf("sock_tcp_send: connection still in progress\n");
  625. #endif
  626.         if (sp->nonblocking)
  627.             return(sock_err(EALREADY));
  628.             
  629.         /* async connect and sync send? */
  630. #if SOCK_TCP_DEBUG >= 5
  631.         dprintf("sock_tcp_send: spinning on connect\n");
  632. #endif
  633.         while(sp->sstate == SOCK_STATE_CONNECTING)
  634.         {
  635. #if SOCK_TCP_DEBUG >= 5
  636.             tcpCheckNotify();
  637. #endif
  638.             SPIN(false,SP_MISC,0L)
  639.         }
  640. #if SOCK_TCP_DEBUG >= 5
  641.         dprintf("sock_tcp_send: done spinning\n");
  642. #endif
  643.     }
  644.         
  645.     /* socket is not connected */
  646.     if (! (sp->sstate == SOCK_STATE_CONNECTED)) 
  647.     {
  648.         /* see if a previous operation failed */
  649.         if (sp->sstate == SOCK_STATE_UNCONNECTED && sp->asyncerr != 0)
  650.         {
  651.             (void) sock_err(sp->asyncerr);
  652.             sp->asyncerr = 0;
  653.             return(-1);
  654.         }
  655.  
  656.         /* I guess he just forgot */
  657.         return(sock_err(ENOTCONN));        
  658.     }
  659.     
  660.     if ( (xTCPBytesWriteable(sp) < count) && (sp->nonblocking) )
  661.         return sock_err(EWOULDBLOCK);
  662.         
  663.     bytes=count;    /* save count before we nuke it */
  664.     sock_memset(wdsarray,0,TCP_MAX_WDS*sizeof(miniwds));    /* clear up terminus and mark empty */
  665.     thiswds = wdsarray;
  666.     wdsnum = 0;
  667.     
  668.     while (count > 0)
  669.         {    
  670.         /* make sure the thing that just finished worked ok */
  671.         if (sp->asyncerr != 0)
  672.             {
  673.             (void) sock_err(sp->asyncerr);
  674.             sp->asyncerr = 0;
  675.             return(-1);
  676.             }
  677.  
  678. /*
  679.  * for deBUGging: try replacing TCP_MAX_MSG with a small value (like 7) so
  680.  * you can test that the loop won't choke while waiting for writes to finish
  681.  */
  682.         towrite=min(count,TCP_MAX_MSG);
  683.         
  684.         /* find a clean wds */
  685.         
  686.         while ( thiswds->length != 0 )
  687.             {
  688.             wdsnum = (short)((wdsnum+1)%TCP_MAX_WDS); /* generates compiler warning w/o short - why? */
  689.             if (wdsnum)
  690.                 thiswds++;
  691.             else
  692.                 thiswds = wdsarray;
  693.             SPIN(false,SP_TCP_WRITE,count);    /* spin once */
  694.             }
  695.         
  696.         /* find a clean pb */
  697.         
  698.         if (!(pb= (TCPiopb *)sock_fetch_pb(sp)))
  699.             return sock_err(ENOMEM);
  700.             
  701.         
  702.         thiswds->length = (short)towrite;
  703.         thiswds->ptr=buffer;
  704.                 
  705.         xTCPSend(pb,(wdsEntry *)thiswds,(count <= TCP_MAX_MSG), /* push */
  706.                 flags & MSG_OOB,    /*urgent*/
  707.                  (TCPIOCompletionProc) sock_tcp_send_done);
  708.                 
  709.         SPIN(false,SP_TCP_WRITE,count);
  710.         count -= towrite;
  711.         buffer += towrite;
  712.         }
  713.         
  714.     SPIN(pb->ioResult == inProgress,SP_TCP_WRITE,0);
  715.     
  716.     if ( pb->ioResult == noErr )
  717.         {
  718.         return(bytes);
  719.         }
  720.     else
  721.         return(sock_err(pb->ioResult));
  722. }
  723.  
  724.  
  725. /*
  726.  *    sock_tcp_can_write() - returns non-zero if a write will not block
  727.  * Very lousy check. Need to check (send window)-(unack data).
  728.  */
  729. int sock_tcp_can_write(
  730.     SocketPtr sp)
  731. {
  732.     return (sp->sstate == SOCK_STATE_CONNECTED);
  733. }
  734.  
  735. /*
  736.  *    sock_tcp_close() - close down a socket being careful about i/o in progress
  737.  */
  738. int sock_tcp_close(
  739.     SocketPtr sp)
  740. {
  741.     OSErr io;
  742.     TCPiopb    *pb;
  743.  
  744.  void sock_flush_out(SocketPtr);
  745.  void sock_flush_in(SocketPtr);
  746.     
  747. #if SOCK_TCP_DEBUG >= 2
  748.     sock_print("sock_tcp_close ",sp);
  749. #endif
  750.     
  751.     if (!(pb= (TCPiopb *)sock_fetch_pb(sp)))
  752.         return sock_err(ENOMEM);
  753.         
  754.     sock_flush_out(sp);
  755.     
  756.     /* close the stream */ 
  757.     io = xTCPClose(pb,(TCPIOCompletionProc)(-1));
  758.     
  759. #if SOCK_TCP_DEBUG >= 5
  760.     dprintf("sock_tcp_close: xTCPClose returns %d\n",io);
  761. #endif
  762.  
  763.     switch (io)
  764.     {
  765.         case noErr:
  766.         case connectionClosing:
  767.             break;
  768.         case connectionDoesntExist:
  769.         case connectionTerminated:
  770.             break;
  771.         case invalidStreamPtr:
  772.         default:
  773.             return(sock_err(io));
  774.     }
  775.     
  776.     sock_flush_in(sp);
  777.     
  778.     /* destroy the stream */ 
  779.     if ((io = xTCPRelease(pb)) != noErr)
  780.     {
  781. #if SOCK_TCP_DEBUG >= 5
  782.         dprintf("sock_tcp_close: xTCPRelease error %d\n",io);
  783. #endif
  784.         return(sock_err(io));
  785.     }
  786.     /* check for errors from async writes etc */
  787.     if (( sp->asyncerr != noErr ) && ( sp->asyncerr != connectionTerminated ))
  788.     {
  789. #if SOCK_TCP_DEBUG >= 5
  790.         dprintf("sock_tcp_close: asyncerr %d\n",sp->asyncerr);
  791. #endif
  792.         return(sock_err(sp->asyncerr));
  793.     }
  794.     return(0);
  795. }
  796.  
  797. /* int defaultSpin(spin_msg msg,long param) */
  798.  
  799. static void sock_flush_out(SocketPtr sp) {
  800.     while (xTCPWriteBytesLeft(sp)>0) {
  801.         (*spinroutine)( SP_MISC,0L);
  802.         }
  803.     }
  804.  
  805. static void sock_flush_in(SocketPtr sp) {
  806.     TCPiopb    *pb;
  807.     rdsEntry    rdsarray[TCP_MAX_WDS+1];
  808.     int        passcount;
  809.     const int maxpass =4;
  810.     
  811.     if (!(pb = (TCPiopb *)sock_fetch_pb(sp)))
  812.         return;    
  813.         
  814.     for (passcount=0;passcount<maxpass;passcount++) {
  815.         if (xTCPNoCopyRcv(pb,rdsarray,TCP_MAX_WDS,1,0)==noErr) {
  816.             xTCPBufReturn(pb,rdsarray,0);
  817.             (*spinroutine)( SP_MISC,0L);
  818.             }
  819.         else
  820.             break;
  821.         }
  822.         
  823.     if (passcount == maxpass) {        /* remote side isn't being nice */
  824.         (void)xTCPAbort(pb);        /* then try again */
  825.         
  826.         for (passcount=0;passcount<maxpass;passcount++) {
  827.             if (xTCPNoCopyRcv(pb,rdsarray,TCP_MAX_WDS,1,0)==noErr) {
  828.                 xTCPBufReturn(pb,rdsarray,0);
  829.                 (*spinroutine)( SP_MISC,0L);
  830.                 }
  831.             else
  832.                 break;
  833.             }
  834.         }
  835.     }
  836.     
  837.  
  838. #ifndef COMP_CODEWAR
  839. #pragma segment SOCK_RESIDENT
  840. #endif
  841. /*
  842.  * Interrupt routines - MUST BE IN A RESIDENT SEGMENT! Most important to 
  843.  * MacApp programmers
  844.  */
  845. pascal void sock_tcp_notify(
  846.     StreamPtr tcpStream,
  847.     unsigned short eventCode,
  848.     Ptr userDataPtr,
  849.     unsigned short terminReason,
  850.     struct ICMPReport *icmpMsg)
  851.     {
  852. #pragma unused (userDataPtr,terminReason,icmpMsg)
  853.     register     StreamHashEntPtr    shep;
  854.     
  855.  
  856.     shep = sock_find_shep(tcpStream);
  857.     if ( shep )
  858.         {
  859.         SocketPtr    sp = shep->socket;
  860.         
  861.         if ( eventCode == TCPClosing )
  862.             {
  863.             sp->sstate = SOCK_STATE_CLOSING;
  864.             }
  865.         else if ( eventCode == TCPTerminate )
  866.             {
  867.             sp->sstate = SOCK_STATE_UNCONNECTED;
  868.             }
  869.         }
  870.     }
  871.  
  872. static void sock_tcp_connect_done(TCPiopb *pb)
  873.     {
  874.     SocketPtr sp;
  875.     
  876.     sp = sock_find_shep(pb->tcpStream)->socket;
  877.     
  878.     if (pb->ioResult == noErr )
  879.         {
  880.         sp->sa.sin_addr.s_addr = pb->csParam.open.localHost;
  881.         sp->sa.sin_port = pb->csParam.open.localPort;
  882.         sp->peer.sin_addr.s_addr = pb->csParam.open.remoteHost;
  883.         sp->peer.sin_port = pb->csParam.open.remotePort;
  884.         sp->sstate = SOCK_STATE_CONNECTED;
  885.         sp->asyncerr = noErr;
  886.         }
  887.     }
  888.  
  889.  
  890. static void
  891. sock_tcp_listen_done(TCPiopb *pb)
  892. {    
  893.     SocketPtr sp;
  894.  
  895.     sp = sock_find_shep(pb->tcpStream)->socket;
  896.     
  897.     switch(pb->ioResult)
  898.     {
  899.         case noErr:
  900.             sp->peer.sin_addr.s_addr = pb->csParam.open.remoteHost;
  901.             sp->peer.sin_port = pb->csParam.open.remotePort;
  902.             sp->sstate = SOCK_STATE_LIS_CON;
  903.             sp->asyncerr = 0;
  904.             break;
  905.             
  906.         case openFailed:
  907.         case invalidStreamPtr:
  908.         case connectionExists:
  909.         case duplicateSocket:
  910.         case commandTimeout:
  911.         default:                
  912.             sp->sstate = SOCK_STATE_UNCONNECTED;
  913.             sp->asyncerr = pb->ioResult;
  914.             break;
  915.     }
  916. }
  917.  
  918. static void sock_tcp_recv_done( TCPiopb *pb)
  919.     {
  920.     register    readin;
  921.     register    SocketPtr    sp;
  922.  
  923.     sp = sock_find_shep( pb->tcpStream )->socket;;
  924.     
  925.     if (pb->ioResult == noErr)
  926.         {
  927.         readin = pb->csParam.receive.rcvBuffLen;
  928.         sp -> recvBuf += readin;
  929.         sp -> recvd   += readin;
  930.         sp -> torecv  -= readin;
  931.         if ( sp -> torecv )
  932.             {
  933.             xTCPRcv(pb,sp->recvBuf,min(sp -> torecv,TCP_MAX_MSG),1,
  934.                     (TCPIOCompletionProc)sock_tcp_recv_done);
  935.             }
  936.         }
  937.     }
  938.  
  939.  
  940. static void sock_tcp_send_done( TCPiopb *pb)
  941. {    
  942.     SocketPtr sp;
  943.  
  944.     sp = sock_find_shep(pb->tcpStream)->socket;
  945.     
  946.     switch(pb->ioResult)
  947.     {
  948.         case noErr:
  949.             ((wdsEntry *)(pb->csParam.send.wdsPtr))->length = 0;    /* mark it free */
  950.             break;
  951.             
  952.         case ipNoFragMemErr:
  953.         case connectionClosing:
  954.         case connectionTerminated:
  955.         case connectionDoesntExist:
  956.             sp->sstate = SOCK_STATE_UNCONNECTED;
  957.             sp->asyncerr = ENOTCONN;
  958.             break;
  959.             
  960.         case ipDontFragErr:
  961.         case invalidStreamPtr:
  962.         case invalidLength:
  963.         case invalidWDS:
  964.         default:
  965.             sp->sstate = SOCK_STATE_UNCONNECTED;
  966.             sp->asyncerr = pb->ioResult;
  967.             break;
  968.     }
  969.     
  970. }
  971.  
  972.